import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthenticationError, ApolloError } from 'apollo-server';

@Injectable()
export class AuthenticationGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const authorities = this.reflector.get<string[]>(
      'authorities',
      context.getHandler(),
    );

    if (!authorities) {
      return true;
    }

    const ctx = GqlExecutionContext.create(context);
    const auth = ctx.getContext().request.session.auth;
    if (!auth) {
      throw new AuthenticationError('用户未登录');
    }

    const userAuthorities = auth.authorities;
    const hasAuthority = () =>
      userAuthorities
        .map(({ name }) => name)
        .some((authority: string) => authorities.includes(authority));
    if (!userAuthorities || (userAuthorities && !hasAuthority())) {
      throw new ApolloError('资源无权访问', 'UNAUTHORIZED');
    }

    return true;
  }
}
